﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using DocumentsTagger.BL;
using DocumentsTagger.DAL;
using DocumentsTagger.DM;
using System.Threading;
using DocumentsTagger.MatchingAlgorithms;

namespace DocumentsTagger
{
    /// <summary>
    /// This is the tagging engine for the application.
    /// the tagger is given access to the DL and BL layers , and operates with them autonomiously.
    /// its job: search for available tags in the data stores.
    ///          foreach one - check if it has enough data to work on specific tag
    ///          if so, hands the learning job to a MatchingAlgorithm.
    ///          after learning, match other documents to for the tag (by MatchingAlgorithm)
    ///          if match is valid, add the tag to suggestion tags list.
    /// </summary>
    public class Tagger
    {
        private const int MILLISECOND = 1;
        private const int SECOND = 1000 * MILLISECOND;             // 1 sec = 1000 milli
        private const int MINUTE = 60*SECOND;                       // 1 min = 60 sec
        private const int INTERMISSION_TIME = 5 * SECOND;           // we dont want to overwork the CPU.
        private const int MINIMAL_DOCUMENTS_TO_TAG = 3;             // making conclusion on very few test case is not good...
        private readonly TaggerBL r_TaggerBL;
        
        public bool StopRequested { get; private set; }
        /// <summary>
        /// creates an instance of the tagger
        /// </summary>
        public Tagger()
        {
            r_TaggerBL = new TaggerBL(TaggerXMLDal.GetInstance());
            StopRequested = false;
        }

        /// <summary>
        /// starts the tagging mechanizm.
        /// </summary>
        public void StartTagging()
        {
            DateTime lastRun;
            while (StopRequested == false)
            {
                lastRun = DateTime.Now;
                foreach (Tag tag in r_TaggerBL.GetTags())
                {
                    List<Document> GoodDocs = r_TaggerBL.GetDocumentsByAcceptedTag(tag);
                    List<Document> BadDocs = r_TaggerBL.GetDocumentsByDeclinedTag(tag);
                    if ((GoodDocs.Count >= MINIMAL_DOCUMENTS_TO_TAG) && (BadDocs.Count >= MINIMAL_DOCUMENTS_TO_TAG))
                    {
                        matchTag(tag, GoodDocs, BadDocs);

                        if (this.StopRequested) // if the user asked to stop the tagger, do not continue to learn other
                                                // documents
                        {
                            break;
                        }
                    }
                    
                }

                TimeSpan ts = DateTime.Now - lastRun;

                // we do not over work the cpu, to give the user better feeling...
                while ((ts.TotalMilliseconds <= INTERMISSION_TIME)&&(!this.StopRequested))
                {
                    Thread.Sleep((int)(0.5 * SECOND));
                    ts = DateTime.Now - lastRun;
                }
            }
        }

        private void matchTag(Tag tag, List<Document> GoodDocs, List<Document> BadDocs)
        {
            tag.BadWords.Clear();
            tag.GoodWords.Clear();
            BetterBayesian taggerAlgo = new BetterBayesian();
            List<Document> allDocs = r_TaggerBL.GetDocuments();
            bool isMatch;
            taggerAlgo.Learn(GoodDocs, BadDocs);
            tag.DocumentsLearnt = GoodDocs.Count + BadDocs.Count;

            if (taggerAlgo.GoodWords.Count < 5)
            { 
                // nothing to match - too few data...
                return;
            }

            foreach (Document docToMatch in allDocs)
            {
                if (this.StopRequested)
                {
                    break;
                }
                if ((!GoodDocs.Contains(docToMatch)) && (!BadDocs.Contains(docToMatch)))
                {
                    isMatch = taggerAlgo.IsMatch(docToMatch);
                    if ((isMatch) && (!docToMatch.SuggestedTags.Contains(tag)))
                    {
                        r_TaggerBL.AddSuggestedTagToDocument(docToMatch, tag);
                    }
                    else if ((!isMatch) && (docToMatch.SuggestedTags.Contains(tag)))
                    {
                        r_TaggerBL.RemoveSuggestedTag(docToMatch, tag);
                    }
                    
                }
                
            }

            foreach (String word in taggerAlgo.GoodWords)
            {
                tag.AddGoodWord(word);
            }

            foreach (String word in taggerAlgo.BadWords)
            {
                tag.AddBadWord(word);
            }

            foreach (String word in taggerAlgo.GoodWords)
            {
                tag.AddGoodWord(word);
            }
            
        }


        /// <summary>
        /// stops the tagging mechanizm - IT IS NOT IMMIDIATE!!!
        /// </summary>
        public void StopTagging()
        {
            this.StopRequested = true;
        }

        
    }
}
